package com.bxl.chooser.client;

import com.bxl.chooser.bean.ChooseMsg;
import com.bxl.chooser.bean.ChooseResponseMsg;
import com.bxl.chooser.factory.ChooseMsgFactory;
import com.bxl.chooser.util.MessageRegister;
import com.bxl.netty.rpc.client.common.client.ClientInfo;
import com.bxl.netty.rpc.common.future.CompletableFuture;
import com.bxl.netty.rpc.common.message.PackageMsg;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * <>
 *
 * @author baixl
 * @date 2021/3/29
 */
public class Client {
    private Logger logger = LoggerFactory.getLogger(Client.class);
    private ClientInfo leaderClient;
    private ChooseClientContext clientContext;
    private List<ClientInfo> configClient;

    public Client(String ip, int port) {
        this.clientContext = new ChooseClientContextImpl();
        this.clientContext.init();
        configClient = new ArrayList<>();
        configClient.add(new ClientInfo("CHOOSE", ip, port, clientContext));
    }

    public void addClient(String ip, int port) {
        configClient.add(new ClientInfo("CHOOSE", ip, port, clientContext));
    }

    /**发送消息
     * @param content
     * @return
     */
    public ChooseResponseMsg sengMsg(String content) {
        if (this.leaderClient == null) {
            this.leaderClient = configClient.get(new Random().nextInt(configClient.size()));
        }
        PackageMsg<ChooseMsg> registerMsg = ChooseMsgFactory.createClientMsg(7, content, "");
        return sengMsg(registerMsg, 0, 0);
    }

    /**
     * 查询消息
     * @param messageId
     * @return
     */
    public ChooseResponseMsg qryMsg(String messageId) {
        if (this.leaderClient == null) {
            this.leaderClient = configClient.get(new Random().nextInt(configClient.size()));
        }
        PackageMsg<ChooseMsg> registerMsg = ChooseMsgFactory.createClientMsg(8, "", messageId);
        return sengMsg(registerMsg, 0, 0);
    }


    private ChooseResponseMsg sengMsg(PackageMsg<ChooseMsg> registerMsg, int index, int curIndex) {
        ChooseResponseMsg responseMsg = null;

        if (curIndex >= configClient.size()) {
            throw new RuntimeException("服务不可用");
        }
        if (index >= 3) {
            this.leaderClient = configClient.get(curIndex);
            curIndex++;
            index = 0;
        }
        NioSocketChannel client = this.leaderClient.getClient();
        CompletableFuture<ChooseResponseMsg> cf = new CompletableFuture<ChooseResponseMsg>();
        MessageRegister.register(registerMsg.getHeader().getRequestID(), cf);
        try {
            client.writeAndFlush(registerMsg);
            responseMsg = cf.get();
            if (responseMsg != null) {
                //如果不是leader，重新发送给leader
                if (ChooseResponseMsg.change.getCode().equals(responseMsg.getCode())) {
                    leaderClient = new ClientInfo("CHOOSE", responseMsg.getLeaderIP(), responseMsg.getPort(), clientContext);
                    responseMsg = sengMsg(registerMsg, index, curIndex);

                } else if (ChooseResponseMsg.retry.getCode().equals(responseMsg.getCode())) {//无主状态
                    //等待10秒中重试
                    logger.warn("无主状态");
                    TimeUnit.SECONDS.sleep(30);
                    index++;
                    responseMsg = sengMsg(registerMsg, index, curIndex);
                } else if (ChooseResponseMsg.fail.getCode().equals(responseMsg.getCode())) { //写入失败-可能是网络分区，导致失败。重试
                    index++;
                    logger.warn("写入失败");
                    responseMsg = sengMsg(registerMsg, index, curIndex);
                }
            } else {
                curIndex++;
                //直接切换主机
                responseMsg = sengMsg(registerMsg, 4, curIndex);
            }
        } catch (Exception e) {
            //可能是主机挂掉了
            logger.error(e.getMessage());
            curIndex++;
            if (curIndex < configClient.size()) {
                //直接切换主机
                responseMsg = sengMsg(registerMsg, 4, curIndex);
            }
        }
        return responseMsg;
    }
}
